#include "bitmap.h"

/* DIB */
struct _Bitmap
{
	LONG iWidth;
	LONG iHeight;
	BITMAPINFO* pBmpInfo;
	PBYTE pBits;
};

Bitmap* Bitmap_new(LPTSTR lpstrFile)
{
	HANDLE hFile;
	DWORD dwFileSize, dwBytesRead;
	LARGE_INTEGER liFileSize;
	BOOL bSuccess;
	PBYTE pFile;
	BITMAPFILEHEADER* pBmpFileHeader;
	BITMAPINFO* pBmpInfo;
	BITMAPINFOHEADER* pBmpInfoHead;
	PBYTE pBits;

	/* Bitmap object to return */
	Bitmap* bitmap;

	hFile = CreateFile(lpstrFile, GENERIC_READ, FILE_SHARE_READ, 
		(LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING, 
		FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
	if (hFile == INVALID_HANDLE_VALUE) {
		return NULL;
	}

	if (!GetFileSizeEx(hFile, &liFileSize)) {
		CloseHandle(hFile);
		return NULL;
	}

	if (liFileSize.HighPart) {
		/* Cannot deal with large files. */
		CloseHandle(hFile);
		return NULL;
	}

	dwFileSize = liFileSize.LowPart;
	pFile = (PBYTE)malloc(dwFileSize * sizeof(BYTE));
	/* FIXME: Calling malloc may fail. */

	bSuccess = ReadFile(hFile, pFile, dwFileSize, &dwBytesRead, NULL);
	/* FIXME: This may fail, too. */
	pBmpFileHeader = (BITMAPFILEHEADER*)pFile;
	pBmpInfo = (BITMAPINFO*)(pBmpFileHeader + 1);
	pBmpInfoHead = (BITMAPINFOHEADER*)pBmpInfo;

	switch(pBmpInfoHead->biSize) {
		case sizeof(BITMAPINFOHEADER):
			break;
		case sizeof(BITMAPCOREHEADER):
		case sizeof(BITMAPV4HEADER):
		case sizeof(BITMAPV5HEADER):
			/* Only BITMAPINFOHEADER is supported. */
			free(pFile);
			return NULL;
		default:
			/* Unknown header size */
			free(pFile);
			return NULL;
	} /* FIXME: setjmp */

	if (pBmpInfoHead->biCompression != BI_RGB) {
		/* Only uncompressed format is supported. */
		/* TODO: throws exception */
	}

	pBits = (BYTE*)pBmpFileHeader + pBmpFileHeader->bfOffBits;

	bitmap = (Bitmap*)malloc(sizeof(*bitmap));
	bitmap->iWidth = pBmpInfoHead->biWidth;
	bitmap->iHeight = pBmpInfoHead->biHeight;
	bitmap->pBmpInfo = pBmpInfo;
	bitmap->pBits = pBits;

	return bitmap;
}

void Bitmap_show(Bitmap* bitmap, HDC hdc)
{
	int width, height;
	width = bitmap->iWidth;
	height = bitmap->iHeight;

	SetDIBitsToDevice(
		hdc, 
		/* xDst */
		(GetSystemMetrics(SM_CXSCREEN) - width) / 2, 
		/* yDst */
		(GetSystemMetrics(SM_CYSCREEN) - height) / 2, 
		width,				/* cxSrc */
		height,				/* cySrc */
		0,					/* xSrc */
		0,					/* ySrc */
		0,					/* first scan line */
		height,				/* number of scan lines */
		bitmap->pBits, 
		bitmap->pBmpInfo, 
		DIB_RGB_COLORS);
}

void Bitmap_free(Bitmap* bitmap)
{
	free(bitmap->pBmpInfo);
	free(bitmap);
}